package com.gitee.wsl.ext.string


/**
 * Splits a camel cased word into single words
 */
fun String.camelCaseSplit(): List<String> {

    val trimmed = trim()

    if (trimmed.isEmpty()) {
        return emptyList()
    }

    val result = mutableListOf<String>()

    val aToZCaps = 'A'..'Z'

    var lastStart = 0

    for (i in 1 until trimmed.length) {
        val c = trimmed[i]

        if (c in aToZCaps) {
            result.add(
                trimmed.substring(lastStart, i).trim()
            )
            lastStart = i
        }
    }

    if (lastStart < trimmed.length) {
        result.add(
            trimmed.substring(lastStart).trim()
        )
    }

    return result
}

/**
 * Splits a camel case word and joins the parts using the divider
 */
fun String.camelCaseDivide(divider: String = " "): String = camelCaseSplit().joinToString(divider)

/**
 * 切换给定字符串中的大小写。大写转小写，小写转大写。
 *
 * <pre>
 * CharSequenceUtil.swapCase(null)                 = null
 * CharSequenceUtil.swapCase("")                   = ""
 * CharSequenceUtil.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
</pre> *
 *
 * @param str 字符串
 * @return 交换后的字符串
 * @since 4.3.2
 */
fun String.swapCase( ): String {
    if (isEmpty()) {
        return this
    }

    val buffer = toCharArray()

    for (i in buffer.indices) {
        val ch = buffer[i]
        if (ch.isUpperCase()) {
            buffer[i] = ch.lowercaseChar()
        } else if (ch.isTitleCase()) {
            buffer[i] = ch.lowercaseChar()
        } else if (ch.isLowerCase()) {
            buffer[i] = ch.uppercaseChar()
        }
    }
    return buffer.concatToString()
}

/**
 * 给定字符串中的字母是否全部为大写，判断依据如下：
 *
 * <pre>
 * 1. 大写字母包括A-Z
 * 2. 其它非字母的Unicode符都算作大写
</pre> *
 *
 * @param str 被检查的字符串
 * @return 是否全部为大写
 * @since 4.2.2
 */
fun CharSequence.isUpperCase(): Boolean = all{ it.isUpperCase() }


/**
 * 给定字符串中的字母是否全部为小写，判断依据如下：
 *
 * <pre>
 * 1. 小写字母包括a-z
 * 2. 其它非字母的Unicode符都算作小写
</pre> *
 *
 * @param str 被检查的字符串
 * @return 是否全部为小写
 * @since 4.2.2
 */
fun CharSequence.isLowerCase(): Boolean = all{ it.isLowerCase() }


/**
 * 将字符串以智能方式首字母小写（处理驼峰与下划线等情况）。
 * 示例：
 * - "FooBar" -> "fooBar"
 * - "FOOBar" -> "fooBar"
 * - "FOO" -> "foo"
 * - "FOO_BAR" -> "fooBar"
 * - "__F_BAR" -> "fBar"
 */
fun String.decapitalizeSmart(asciiOnly: Boolean = false): String {
    return decapitalizeWithUnderscores(this, asciiOnly)
        ?: decapitalizeSmartForCompiler(asciiOnly)
}

/**
 * 根据下划线分隔的单词列表进行智能小写化转换；如果字符串不包含下划线或只有一个词则返回 null。
 * 示例：
 * - "FOO_BAR" -> "fooBar"
 * - "FOO_BAR_BAZ" -> "fooBarBaz"
 * - "__F_BAR" -> "fBar"
 */
private fun decapitalizeWithUnderscores(str: String, asciiOnly: Boolean): String? {
    val words = str.split("_").filter { it.isNotEmpty() }

    if (words.size <= 1) return null

    val builder = StringBuilder()

    words.forEachIndexed { index, word ->
        if (index == 0) {
            builder.append(toLowerCase(word, asciiOnly))
        } else {
            builder.append(toUpperCase(word.first().toString(), asciiOnly))
            builder.append(toLowerCase(word.drop(1), asciiOnly))
        }
    }

    return builder.toString()
}

private fun toLowerCase(string: String, asciiOnly: Boolean): String {
    return if (asciiOnly) string.toLowerCaseAsciiOnly() else string.lowercase()
}

private fun toUpperCase(string: String, asciiOnly: Boolean): String {
    return if (asciiOnly) string.toUpperCaseAsciiOnly() else string.uppercase()
}

/**
 * 为编译器提供的智能小写化处理：当字符串以大写字母序列开头时，保留语义正确的分界并小写前缀。
 * 示例：
 * - "FooBar" -> "fooBar"
 * - "FOOBar" -> "fooBar"
 * - "FOO" -> "foo"
 */
fun String.decapitalizeSmartForCompiler(asciiOnly: Boolean = false): String {
    if (isEmpty() || !isUpperCaseCharAt(0, asciiOnly)) return this

    if (length == 1 || !isUpperCaseCharAt(1, asciiOnly)) {
        return if (asciiOnly) decapitalizeAsciiOnly() else replaceFirstChar(Char::lowercaseChar)
    }

    val secondWordStart = (indices.firstOrNull { !isUpperCaseCharAt(it, asciiOnly) } ?: return toLowerCase(this, asciiOnly)) - 1

    return toLowerCase(substring(0, secondWordStart), asciiOnly) + substring(secondWordStart)
}

private fun String.isUpperCaseCharAt(index: Int, asciiOnly: Boolean): Boolean {
    val c = this[index]
    return if (asciiOnly) c in 'A'..'Z' else c.isUpperCase()
}